Módulo 3 - Manipulación de datos con tidyverse
2024 -Módulo 3
Capítulo 3 de r4ds 2da edición, 2023
Transformación de datos con dplyr:
Introduce una forma de trabajo que hace relativamente más sencillo trabajar con datos
Principal funcionalidad es un conjunto de verbos que facilitan la transformación de datos. Qué se pueden agrupar según donde se realizan las operaciones.
¿Qué clases de objeto retorna dplyr?
dplyr, case_match()Queremos recodificar class, usamos case_match()
dplyr, case_match()case_match() usa valores para comparar con los de la variable seleccionada
Para caracteres se recodifica directamente con el nombre
dplyr, case_match()dplyr, case_match()dplyr, case_when()case_when() usa expresiones lógicas del lado izquierdo de la fórmula
slice()Selecciona filas por su posición
En este caso selecciona la última fila.
¿Qué hace el siguiente código?
slice()slice_head() y slice_tail() seleccionan la primera y la última fila
slice_min() y slice_max() seleccionan filas con el valor más grande o chico en una variable
slice_sample() selecciona filas aleatoriamente
dplyrMuchas veces quisieramos realizar operaciones sobre una selección de variables que cumplen una determinada condición.
Ejemplo: realizar una operación sobre todas las variables numéricas, o a todas los nombres de la variables de fecha agregarles _Date al final, o calcular medidas de resumen sólo sobre determinadas variables, etc.
dplyr, across()Operar en múltiples columnas al mismo tiempo mediante across()
across() hace sencillo aplicar la misma transformación a muchas columnas usando la misma semántica que select() dentro de funciones como summarise() y mutate()
across tiene dos argumentos básicos:
.cols, se pueden seleccionar variables por nombre, posición o tipo (tipo el usado en select())across() es .fns, que es una función o lista de ellas que se aplican a cada columna. Puede usarse con estilo fórmuladplyr, across()Equivalente usando across y variables seleccionadas por nombre
dplyr, across()Equivalente usando across y variables seleccionadas por posición
dplyr, across()mtcars |>
group_by(cyl, disp) |>
summarise(across(.cols = c(wt,mpg,drat),
.fns = mean,na.rm = TRUE,
.names = "{col}_mean")
)# A tibble: 27 × 5
# Groups: cyl [3]
cyl disp wt_mean mpg_mean drat_mean
<dbl> <dbl> <dbl> <dbl> <dbl>
1 4 71.1 1.84 33.9 4.22
2 4 75.7 1.62 30.4 4.93
3 4 78.7 2.2 32.4 4.08
4 4 79 1.94 27.3 4.08
5 4 95.1 1.51 30.4 3.77
6 4 108 2.32 22.8 3.85
7 4 120. 2.46 21.5 3.7
8 4 120. 2.14 26 4.43
9 4 121 2.78 21.4 4.11
10 4 141. 3.15 22.8 3.92
# ℹ 17 more rows
dplyr, across()Podemos transformar cada variable con más de una función nombradas en una lista
dplyr, across()En mpg hay variables numéricas y categóricas, si queremos hacer lo mismo para las columnas que son numéricas podemos usar where(is.numeric())
where() es una función de ayuda que selecciona las variables para las que una función retorna TRUE.
dplyr, across()También se puede escribir como fórmula
dplyr, La mayoría de los verbos de dplyr usan tidy evaluations, que es un caso especial de non-standard evaluation usanda en tidyverse dos formas en dplyr:
arrange(), count(), filter(), group_by(), mutate(), y summarise() usan data masking entonces uno puede usar variables como si estuvieran en el env, ejemplo usamos mi_var en vez de datos$mi_var.
across(), relocate(), rename(), select(), y pull() usan tidy selection podemos seleccionar variables basadas en su posición, nombre o tipo, ejemplo starts_with("x") o is.numeric.
dplyr, El principal desafío programando con funciones que usan data masking es cuando queremos obtener la variable de una variable en el env. en vez de poner directamente el nombre de la variable.
Dos casos:
Cuando tengo una variable en un argumento de una función ebemos poner el argumento con llaves dobles {{}}. Ejemplo filter(datos, {{ var }}).
Cuando tenemos una env-variable que es un vector character. veremos luego
https://dplyr.tidyverse.org/articles/programming.html
dplyr, across()Uso programáticamente
resultado <- function(dato, numeric_cols = NULL, ...) {
dato |>
group_by(...) |>
summarise(across({{numeric_cols}}, list(
mean = ~mean(.x, na.rm = TRUE),
sd = ~sd(.x, na.rm = TRUE),
q05 = ~quantile(.x, 0.05, na.rm = TRUE),
q95 = ~quantile(.x, 0.95, na.rm = TRUE)
), .names = "{col}_{fn}"))
}
resultado(mpg , numeric_cols = c(cty, hwy),
manufacturer)dplyrExplorar vignette("colwise")
dplyrLograr que tus datos estén en una forma adecuada para la visualización y modelado.
Sin estructurar los datos muchas veces es imposible trabajar con ellos.
Componentes claves:
tibbles son una variante de los data.frames.
Importar datos, leer datos en distintos formatos.
Datos limpios, una forma consistente de guardar tus datos para que sea más sencillo transformar, visualizar y modelar (¿qué es tidy data?).
A que le llamamos datos limpios (tidy data), nos concentraremos en el paquete tidyr de tidyverse.
Ordenando usando pivol_longer() y pivot_wider().
Es importante aprender como organizar nuestros datos en una forma consistente (tidy data)
Los nombres de las columnas son valores no nombres de variables.
Muchas variables están ordenadas en una sola columna.
Muchas unidades observacionales están ordenadas en la misma tabla.
Una sola unidad observacional está en muchas tablas.
Cada variable forma una columna
Cada observación forma una fila
Cada valor debe estar en una celda
Ejemplo del paper Tidy data: paper tidy data
Ventajas:
Si tenés una forma consistente de guardar los datos es más sencillo aprender las herramientas que funcionan con la misma
Tener la variables en columnas son naturalmente manejadas en la forma vectorizada de R. La mayoría de las funciones de R funcionan con valores vectorizados. Esto hace natural transformar la estructura de datos a una limpia (tidy data).
Los datos en general no están limpios así que tenemos que aprender a limpiar!
¿Porqué los datos no están limpios? - La gente no esta familiarizada con el concepto de tidy data y no es sencillo derivarlo solo.
Identificar cuales son variables y cuales observaciones.
Identificar si hay variables que deben ser dispuestas en múltiples columnas.
Identificar si una observación debe ser incluida en múltiples filas.
tidyrpivot_longer() reestructura los datos moviendo columnas a filas.
pivot_wider() reestructura los datos moviendo filas a columnas.
separate divide columnas en múltiples variables.
pivot_longer()Problema común: nombres de columnas no representan nombres de variable, como debería ser, sino que son valores de variables.
Se puede resolver con pivot_longer().
pivot_longer()Genero datos con tibble
pivot_longer()Necesitamos unir (pivot) estas columnas en un nuevo par de variables. Necesitamos describir tres argumentos:
Las columnas cuyos nombres son valores no variables, en este caso tratamientoa y tratamientob, argumento cols
Un nombre para la variable cuyo valor está en el nombre de la columna, en este caso le podemos llamar tratamiento, argumento names_to
Un nombre para la variable cuyos valores están en las celdas, en este caso le podemos llamar casos, argumento values_to
pivot_longer()pivot_longer(data, cols, names_to, values_to,...)
data: data.frame a pivotar
cols: Columnas a pivotar en formato largo
names_to: un vector de tipo character que especifica el nombre de la columna o columnas a crear con los datos contenidos en el nombre de la columna
values_to: un string especificando el nombre de la columna a crear con los datos contenidos en las celdas
…: otros argumentos
pivot_longer()pivot_longer()pivot_longer()Resultados equivalentes con pivot_longer()
Las columnas a girar se especifican con el estilo de notación usado en select() (tipo, nombre y posición )
pivotmed <- medico |>
pivot_longer(cols = c("tratamientoa", "tratamientob"),
names_to ="tratamiento" , values_to = "conteo")
pivotmed# A tibble: 6 × 3
nombre tratamiento conteo
<chr> <chr> <dbl>
1 Pablo Gomez tratamientoa NA
2 Pablo Gomez tratamientob 18
3 Mariana Mattos tratamientoa 4
4 Mariana Mattos tratamientob 1
5 Laura Almo tratamientoa 6
6 Laura Almo tratamientob 7
Podríamos combinar con lo que aprendimos con dplyr para recodificar tratamiento
pivotmed <-medico |>
pivot_longer(cols = c("tratamientoa", "tratamientob"),
names_to ="tratamiento" , values_to = "conteo") |>
mutate(tratamiento = case_match(tratamiento, "tratamientoa" ~ "a",
"tratamientob" ~ "b"))
pivotmed# A tibble: 6 × 3
nombre tratamiento conteo
<chr> <chr> <dbl>
1 Pablo Gomez a NA
2 Pablo Gomez b 18
3 Mariana Mattos a 4
4 Mariana Mattos b 1
5 Laura Almo a 6
6 Laura Almo b 7
pivot_wider()pivot_wider() es la operación opuesta a pivot_longer() se usa cuando las observaciones están dispersas en muchas filas. Incrementa el número de columnas y disminuye el de las filas.pivot_wider(data, names_from, values_from, ...)
names_from : La columna que tiene en las observaciones el nombre de las nuevas columnas
values_from:La columna que tiene los valores
...: otros argumentos
pivot_wider()Datos que quiero reestructurar, quiero poner los tratamientos a y b en las columnas
pivot_wider()separate()separate separa una columna en múltiples columnas. Por defecto cualquier símbolo no alfanumérico se usa para separar la columna,Principales parámetros
data: data.frame.
col: el nombre de la columna o posición.
into: nombres de las nuevas variables para crear, vector tipo character.
sep: separador entre columnas
Formalmente si es de tipo character, sep es una expresión regular que veremos en más detalle luego. Si es numeric, sep es interpretado como una posición para separar contando de izquierda a derecha arranca en 1 y de derecha a izquierda de -1.
separate()# A tibble: 4 × 2
x month_year
<chr> <dbl>
1 <NA> 121999
2 a*b 112005
3 a.c 102001
4 a&d 102001
# A tibble: 4 × 3
V1 V2 month_year
<chr> <chr> <dbl>
1 <NA> <NA> 121999
2 a b 112005
3 a c 102001
4 a d 102001
separate()separate()¿Qué pasa con la clase de las nuevas variables en tib2?
# A tibble: 4 × 2
x month_year
<chr> <dbl>
1 <NA> 121999
2 1*b 112005
3 3.c 102001
4 8&d 102001
# A tibble: 4 × 3
V1 V2 month_year
<chr> <chr> <dbl>
1 <NA> <NA> 121999
2 1 b 112005
3 3 c 102001
4 8 d 102001
separate()Deja por defecto el tipo original y en algunos casos no es lo correcto. Uso convert = TRUE lo intenta convertir a un tipo más apropiado.
# A tibble: 4 × 2
x month_year
<chr> <dbl>
1 <NA> 121999
2 1*b 112005
3 3.c 102001
4 8&d 102001
# A tibble: 4 × 3
V1 V2 month_year
<int> <chr> <dbl>
1 NA <NA> 121999
2 1 b 112005
3 3 c 102001
4 8 d 102001
unite()unite() es el inverso de separate(). Pega múltiples columnas en una.
unite(data, col, ...)unite()